/*
 * Copyright (C) 2019-2020 Yaong <yaongtime@gmail.com>
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "vc4_private.h"
#include "vk_format_info.h"
#include "vc4_vk_formats.h"
#include "vc4_memory.h"
#include "pipe/p_state.h"
#include "vc4_tiling.h"

#if 1

/**
 * Returns true if the implementation supports the requested operation (even if
 * it failed to process it, for example, due to an out-of-memory error).
 */
static bool
copy_buffer_to_image(struct vc4_cmd_buffer *cmd_buffer,
                         struct vc4_image *image,
                         struct vc4_buffer *buffer,
                         const VkBufferImageCopy *region)
{
   VkFormat vk_format = image->vk_format;
   // const struct vc4_format *format = vc4_get_format(image->vk_format);

   /* Format must be supported for texturing */
//    if (!vc4_tfu_supports_tex_format(&cmd_buffer->device->devinfo,
//                                      format->tex_type)) {
//       return false;
//    }

   /* Only color formats */
   if (vk_format_is_depth_or_stencil(vk_format))
      return false;

   /* Destination can't be raster format */
   const uint32_t mip_level = region->imageSubresource.mipLevel;
   if (image->slices[mip_level].tiling == VC4_TILING_FORMAT_LINEAR)
      return false;

   /* Region must include full slice */
   // const uint32_t offset_x = region->imageOffset.x;
   // const uint32_t offset_y = region->imageOffset.y;
   // if (offset_x != 0 || offset_y != 0)
   //    return false;

   uint32_t width, height;
   if (region->bufferRowLength == 0)
      width = region->imageExtent.width;
   else
      width = region->bufferRowLength;

   if (region->bufferImageHeight == 0)
      height = region->imageExtent.height;
   else
      height = region->bufferImageHeight;

   if (width != image->extent.width || height != image->extent.height)
      return false;

   const struct vc4_resource_slice *slice = &image->slices[mip_level];

   uint32_t num_layers;
   if (image->type != VK_IMAGE_TYPE_3D)
      num_layers = region->imageSubresource.layerCount;
   else
      num_layers = region->imageExtent.depth;
   assert(num_layers > 0);

   assert(image->mem);
   struct vc4_bo *dst_bo = &image->mem->bo;

   assert(buffer->bo);
   struct vc4_bo *src_bo = buffer->bo;

   if (!dst_bo->map)
      vc4_bo_map(cmd_buffer->device, dst_bo);

   if (!src_bo->map)
      vc4_bo_map(cmd_buffer->device, src_bo);

   const uint32_t buffer_stride = width * image->cpp;
   for (int i = 0; i < num_layers; i++) {
      uint32_t layer = region->imageSubresource.baseArrayLayer + i;

      uint32_t src_offset = buffer->bo_offset + region->bufferOffset;
      void *bsrc = src_bo->map + src_offset +
                   height * buffer_stride * i;

      uint32_t dst_offset = vc4_layer_offset(image, mip_level, layer);
      void *idst = dst_bo->map + dst_offset;

      const struct pipe_box box = {
          .x = region->imageOffset.x,
          .y = region->imageOffset.y,
          .z = region->imageOffset.z,
          .width = region->imageExtent.width,
          .height = region->imageExtent.height,
          .depth = region->imageExtent.depth,
      };

      vc4_store_tiled_image(idst, slice->stride,
                            bsrc, width * image->cpp,
                            slice->tiling, image->cpp,
                            &box);
   }

   return true;
}
#endif

void
vc4_CmdCopyBufferToImage(VkCommandBuffer commandBuffer,
                         VkBuffer srcBuffer,
                         VkImage dstImage,
                         VkImageLayout dstImageLayout,
                         uint32_t regionCount,
                         const VkBufferImageCopy *pRegions)
{
   VC4_FROM_HANDLE(vc4_cmd_buffer, cmd_buffer, commandBuffer);
   VC4_FROM_HANDLE(vc4_buffer, buffer, srcBuffer);
   VC4_FROM_HANDLE(vc4_image, image, dstImage);

   assert(image->samples == VK_SAMPLE_COUNT_1_BIT);

   for (uint32_t i = 0; i < regionCount; i++) {
      copy_buffer_to_image(cmd_buffer,
                           image,
                           buffer,
                           &pRegions[i]);
   }
}

static void
copy_buffer(struct vc4_cmd_buffer *cmd_buffer,
            struct vc4_bo *dst_bo,
            uint32_t dst_offset,
            struct vc4_bo *src_bo,
            uint32_t src_offset,
            const VkBufferCopy *region)
{
   void *src, *dst;

   dst_offset += region->dstOffset;
   src_offset += region->srcOffset;

   if (!src_bo->map)
      vc4_bo_map(cmd_buffer->device, src_bo);

   if (!dst_bo->map)
      vc4_bo_map(cmd_buffer->device, dst_bo);

   src = src_bo->map + src_offset;
   dst = dst_bo->map + dst_offset;

   assert(memcpy(dst, src, region->size));
}

void
vc4_CmdCopyBuffer(VkCommandBuffer commandBuffer,
                  VkBuffer srcBuffer,
                  VkBuffer dstBuffer,
                  uint32_t regionCount,
                  const VkBufferCopy *pRegions)
{
   VC4_FROM_HANDLE(vc4_cmd_buffer, cmd_buffer, commandBuffer);
   VC4_FROM_HANDLE(vc4_buffer, src_buffer, srcBuffer);
   VC4_FROM_HANDLE(vc4_buffer, dst_buffer, dstBuffer);

   for (uint32_t i = 0; i < regionCount; i++) {
     copy_buffer(cmd_buffer,
                 dst_buffer->bo, dst_buffer->bo_offset,
                 src_buffer->bo, src_buffer->bo_offset,
                 &pRegions[i]);
   }
}
